package com.tsfyun.scm.service.impl.user;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.http.HttpUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.anji.captcha.model.common.ResponseModel;
import com.anji.captcha.model.vo.CaptchaVO;
import com.anji.captcha.service.CaptchaService;
import com.google.common.collect.Lists;
import com.tsfyun.common.base.config.properties.DDRemindProperties;
import com.tsfyun.common.base.constant.LoginConstant;
import com.tsfyun.common.base.dto.Result;
import com.tsfyun.common.base.dto.WxNoticeAttr;
import com.tsfyun.common.base.dto.WxNoticeMessageDTO;
import com.tsfyun.common.base.enums.*;
import com.tsfyun.common.base.enums.core.DeviceEnum;
import com.tsfyun.common.base.exception.ServiceException;
import com.tsfyun.common.base.extension.ServiceImpl;
import com.tsfyun.common.base.help.DingTalkNoticeUtil;
import com.tsfyun.common.base.security.LoginVO;
import com.tsfyun.common.base.security.SecurityUtil;
import com.tsfyun.common.base.security.SysRoleVO;
import com.tsfyun.common.base.security.TokenVO;
import com.tsfyun.common.base.util.*;
import com.tsfyun.common.base.vo.declare.RecordEnterprisesVO;
import com.tsfyun.scm.client.DeclareServerClient;
import com.tsfyun.scm.config.redis.RedisUtils;
import com.tsfyun.scm.constant.WeiXinConstant;
import com.tsfyun.scm.dto.user.*;
import com.tsfyun.scm.dto.user.client.ClientChangePassWordDTO;
import com.tsfyun.scm.dto.user.client.ClientSetPassWordDTO;
import com.tsfyun.scm.dto.user.client.ClientUpdatePassWordDTO;
import com.tsfyun.scm.entity.customer.Customer;
import com.tsfyun.scm.entity.system.SysRole;
import com.tsfyun.scm.entity.user.Login;
import com.tsfyun.scm.entity.user.Person;
import com.tsfyun.scm.mapper.user.LoginMapper;
import com.tsfyun.scm.security.config.StringRedisUtils;
import com.tsfyun.scm.service.customer.ICustomerService;
import com.tsfyun.scm.service.system.ISysRoleService;
import com.tsfyun.scm.service.third.IShortMessageService;
import com.tsfyun.scm.service.third.IWxMessageService;
import com.tsfyun.scm.service.user.ILogLoginService;
import com.tsfyun.scm.service.user.ILoginService;
import com.tsfyun.scm.service.user.IPersonService;
import com.tsfyun.scm.support.LoginHelper;
import com.tsfyun.scm.util.LoginUtil;
import com.tsfyun.scm.util.TsfWeekendSqls;
import com.tsfyun.scm.util.WXUtil;
import com.tsfyun.scm.vo.customer.CompanyVO;
import com.tsfyun.scm.vo.support.WeixinFocusScanVO;
import com.tsfyun.scm.vo.user.*;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.map.LinkedMap;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.context.config.annotation.RefreshScope;
import org.springframework.dao.DuplicateKeyException;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.data.redis.serializer.StringRedisSerializer;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import tk.mybatis.mapper.entity.Example;
import tk.mybatis.mapper.weekend.WeekendSqls;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.time.LocalDateTime;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.function.Function;
import java.util.stream.Collectors;

@RefreshScope
@Service
@Slf4j
public class LoginServiceImpl extends ServiceImpl<Login> implements ILoginService {

    @Autowired
    private LoginMapper loginMapper;

    @Autowired
    private IPersonService personService;

    @Autowired
    private ISysRoleService sysRoleService;

    @Autowired
    private IShortMessageService shortMessageService;

    @Value("${auth.expireSecond:3600}")
    private long tokenExpireTime;

    @Autowired
    @Qualifier("loginRedisScript")
    private DefaultRedisScript<Long> loginScript;
    @Autowired
    @Qualifier("stringRedisTemplate")
    private RedisTemplate stringRedisTemplate;
    @Autowired
    protected HttpServletRequest request;
    @Autowired
    private StringRedisUtils stringRedisUtils;
    @Autowired
    private RedisUtils redisUtils;
    @Autowired
    private ILogLoginService logLoginService;
    @Autowired
    private ICustomerService customerService;
    @Autowired
    private ThreadPoolTaskExecutor threadPoolTaskExecutor;
    @Value("${auth.errorPwdShowCodeTimes:3}")
    private int errorPwdShowCodeTimes;
    @Value("${auth.errorPwdShowCodeExpire:5}")
    private int errorPwdShowCodeExpire;
    @Value("${tenant.autoExpire.password:90}")
    private int passwordExporeCheckDays;
    @Value("${tenant.resetpwd.access.expire:5}")
    private long resetPwdCodeExpire;
    @Value("${weChat.wxccxAppid}")
    private String wxccxAppid;
    @Value("${weChat.wxccxSecret}")
    private String wxccxSecret;
    @Resource
    private LoginHelper loginHelper;
    @Autowired(required = false)
    private CaptchaService captchaService;
    @Resource
    private DDRemindProperties ddRemindProperties;
    @Autowired
    private IWxMessageService wxMessageService;
    @Value("${spring.profiles.active:dev}")
    private String profiles;
    @Autowired
    private DeclareServerClient declareServerClient;

    /**
     * 用户登录
     * @param dto
     * @return
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public LoginInfoVO login(LoginDTO dto) {
        //1、获取登录账号信息
        Login login = loginMapper.selectByLoginName(dto.getUserName());
        LoginTypeEnum loginType = LoginTypeEnum.of(dto.getLoginType());
        LoginRoleTypeEnum roleType = LoginRoleTypeEnum.of(dto.getRoleType());
        Person person = null;
        Customer customer = null;
        switch (loginType) {
            case ACCOUNT:// 账号登录
                TsfPreconditions.checkNonNull(login,"当前账号不存在");
                //2、获取登录人员信息
                person = personService.getById(login.getPersonId());
                TsfPreconditions.checkNonNull(person,"登录帐号信息错误");
                TsfPreconditions.checkArgument(Objects.equals(person.getIncumbency(),Boolean.TRUE),new ServiceException("您已禁止登录该系统"));
                TsfPreconditions.checkArgument(!Objects.equals(person.getIsDelete(),Boolean.TRUE),new ServiceException("您已被该企业移除，禁止登录该系统"));
                if(StringUtils.isEmpty(dto.getPassWord())) {
                    throw new ServiceException("登录密码不能为空");
                }
                //判断用户是否设置了登录密码
                if(StringUtils.isEmpty(person.getPassWord())) {
                    throw new ServiceException("您还未设置登录密码，请使用其他登录方式登录后设置登录密码");
                }
                DeviceEnum deviceEnum = DeviceUtil.getPlatform();
                Boolean nowSencondCheck = false;
                //此处优化，如果是客户端且终端是PC则记录密码失败次数
                if(Objects.equals(deviceEnum,DeviceEnum.PC) && Objects.equals(roleType,LoginRoleTypeEnum.CLIENT)) {
                    //获取现在输入密码错误的次数
                    String historyErrorPwd = redisUtils.getToString(LoginConstant.LOGIN_VERIFY + dto.getUserName());
                    log.info("历史输入密码错误次数【{}】，配置输入错误次数【{}】，用户名【{}】",historyErrorPwd,errorPwdShowCodeTimes,dto.getUserName());
                    //当密码输入正确时
                    if(Objects.equals(person.getPassWord(),StringUtils.encrySha((dto.getPassWord())))) {
                        if(StringUtils.isInteger(historyErrorPwd) && Integer.parseInt(historyErrorPwd) >= errorPwdShowCodeTimes ) {
                            if(StringUtils.isEmpty(dto.getCaptchaVerification())) {
                                log.info("本次密码输入正确，但是历史输入错误次数超限，验证码为空，用户名【{}】",dto.getUserName());
                                throw new ServiceException(ResultCodeEnum.PASSWORD_VERIFY_ERROR);
                            }
                            nowSencondCheck = true;
                        }
                    } else {
                        Long passwordErrorCnt = redisUtils.incrementKeepExpire(LoginConstant.LOGIN_VERIFY + dto.getUserName(),1L,errorPwdShowCodeExpire,TimeUnit.MINUTES);
                        log.info("当前输入密码错误次数含本次【{}】，配置输入错误次数【{}】，用户名【{}】",passwordErrorCnt,errorPwdShowCodeTimes,dto.getUserName());
                        if(passwordErrorCnt == errorPwdShowCodeTimes) { //刚好达到配置次数
                            throw new ServiceException(ResultCodeEnum.PASSWORD_NEED_VERIFY_ERROR);
                        }
                        //还需告诉前端弹出滑块验证
                        if(passwordErrorCnt > errorPwdShowCodeTimes) {
                            if(StringUtils.isEmpty(dto.getCaptchaVerification())) {
                                log.info("验证参数为空，用户名【{}】",dto.getUserName());
                                throw new ServiceException(ResultCodeEnum.PASSWORD_NEED_VERIFY_ERROR);
                            }
                            throw new ServiceException(ResultCodeEnum.PASSWORD_MANY_ERROR_VERIFY_ERROR);
                        } else {
                            //没有达到错误次数
                            throw new ServiceException("登录密码输入错误");
                        }
                    }
                } else {
                    if(!Objects.equals(person.getPassWord(),StringUtils.encrySha((dto.getPassWord())))) {
                        throw new ServiceException("登录密码输入错误");
                    }
                }
                if(nowSencondCheck) { //密码输入正确二次验证
                    CaptchaVO captchaVO = new CaptchaVO();
                    captchaVO.setCaptchaVerification(dto.getCaptchaVerification());
                    ResponseModel response = captchaService.verification(captchaVO);
                    if(!Objects.equals(response.getRepCode(),"0000")) {
                        log.info("验证码验证不通过，，用户名【{}】,响应信息【{}】",dto.getUserName(),response);
                        throw new ServiceException(ResultCodeEnum.PASSWORD_VERIFY_ERROR);
                    } else {
                        redisUtils.remove(LoginConstant.LOGIN_VERIFY + dto.getUserName());
                    }
                }
                break;
            case PHONE:// 手机验证码登录
                //验证短信验证码是否正确
                shortMessageService.checkVerificationCode(dto.getUserName(), MessageNodeEnum.COMMON_PROVE,dto.getMessageCode());
                if(Objects.isNull(login)){
                    //用户注册
                    switch (roleType){
                        case CLIENT:// 客户端
                            personService.clientRegister(new RegisterDTO(dto.getUserName()));
                            DingTalkNoticeUtil.send2DingDingBiz("用户注册",String.format("用户通过手机验证码登录，手机号码【%s】",dto.getUserName()),ddRemindProperties.getDingding(),profiles);
                            break;
                        default:
                            throw new ServiceException("当前手机号还未注册");
                    }
                    login = loginMapper.selectByLoginName(dto.getUserName());
                }
                //2、获取登录人员信息
                person = personService.getById(login.getPersonId());
                break;
            case WXXCX://微信小程序(同下)
            case WXGZH://微信公众号
                if(Objects.nonNull(login)){
                    //2、获取登录人员信息
                    person = personService.getById(login.getPersonId());
                    TsfPreconditions.checkNonNull(person,"登录帐号信息错误");
                    TsfPreconditions.checkArgument(Objects.equals(person.getIncumbency(),Boolean.TRUE),new ServiceException("您已禁止登录该系统"));
                    TsfPreconditions.checkArgument(!Objects.equals(person.getIsDelete(),Boolean.TRUE),new ServiceException("您已被该企业移除，禁止登录该系统"));
                }else{
                    LoginInfoVO loginInfoVO = new LoginInfoVO();
                    loginInfoVO.setSuccess(Boolean.FALSE);
                    loginInfoVO.setBindAccessId(dto.getBindAccessId());
                    return loginInfoVO;
                }
                break;
            case WXXCX_PHONE://微信小程序手机号码一键登录
                if(Objects.isNull(login)) { //首次使用微信小程序手机号码一键登录
                    //注册用户登录信息
                    personService.clientRegister(new RegisterDTO(dto.getUserName()));
                    login = loginMapper.selectByLoginName(dto.getUserName());
                    DingTalkNoticeUtil.send2DingDingBiz("用户注册", StrUtil.format("用户通过微信小程序授权手机号码方式登录，手机号码【{}】",dto.getUserName()),ddRemindProperties.getDingding(),profiles);
                }
                //2、获取登录人员信息
                person = personService.getById(login.getPersonId());
                break;
            default:
                throw new ServiceException("暂不支持此登录方式");
        }
        switch (roleType){
            case MANAGER:// 管理端
                TsfPreconditions.checkArgument(person.getIsManager(),new ServiceException("对不起您无管理端登录权限"));
                break;
            case CLIENT:// 客户端
                TsfPreconditions.checkArgument(person.getIsClient(),new ServiceException("对不起您无客户端登录权限"));
                //获取客户信息
                customer = customerService.getById(person.getCustomerId());
                if(Objects.isNull(customer)){
                    throw new ServiceException("未找到您的公司资料，请联系我司");
                }
                break;
            case SALE:// 销售端
                TsfPreconditions.checkArgument(person.getIsSale(),new ServiceException("对不起您无销售端登录权限"));
                break;
        }
        //绑定第三方登录
        if(StringUtils.isNotEmpty(dto.getBindAccessId())){
            Object threeLoginVOObj = redisUtils.get(LoginConstant.TEMP_LOGIN.concat(dto.getBindAccessId()));
            //是否微信公众号通知消息
            Boolean wxNotice = false;
            if(Objects.nonNull(threeLoginVOObj)){
                ThreeLoginVO threeLoginVO = beanMapper.map(threeLoginVOObj, ThreeLoginVO.class);
                // 第三方账号绑定
                if(!login.getLoginName().equals(threeLoginVO.getUnionId())){
                    // 查询人员是否已经绑定
                    Login tLogin = null;
                    String sourceName = "";
                    if("wxxcx".equals(threeLoginVO.getSource()) || "wxgzh".equals(threeLoginVO.getSource())){
                        tLogin = loginMapper.findByPersonIdAndType(person.getId(),4);
                        sourceName = "微信";
                    }
                    if(Objects.nonNull(tLogin)){
                        if(!tLogin.getLoginName().equals(threeLoginVO.getUnionId())){
                            throw new ServiceException(String.format("当前账户已经被其他%s绑定，无法再次绑定",sourceName));
                        }
                    }else{
                        // 登录表中未绑定
                        Login tlogin = loginMapper.selectByLoginName(threeLoginVO.getUnionId());
                        if(Objects.isNull(tlogin)){
                            tlogin = new Login();
                            tlogin.setPersonId(person.getId());
                            tlogin.setLoginName(threeLoginVO.getUnionId());
                            switch (threeLoginVO.getSource()){
                                case "wxxcx"://小程序和公众号一样写入4
                                case "wxgzh":
                                    tlogin.setType(4);
                                    wxNotice = Boolean.TRUE;
                                    break;
                                default:
                                    throw new ServiceException("第三方来源错误");
                            }
                            super.saveNonNull(tlogin);
                        }
                    }
                }
                //同步人员信息
                synchroPerson(person,threeLoginVO);
                //移除缓存临时登录数据
                redisUtils.remove(LoginConstant.TEMP_LOGIN.concat(dto.getBindAccessId()));
                //发送微信通知
                if("wxgzh".equals(threeLoginVO.getSource())
                   && Objects.equals(wxNotice,Boolean.TRUE)) {
                    bindWxLoginNotice(person,threeLoginVO);
                }
            }
        }
        //获取用户角色
        List<SysRole> sysRoles = sysRoleService.getUserRoles(login.getPersonId());
        //生成Token
        String loginToken = StringUtils.uuid();
        TokenVO tokenVO = new TokenVO();
        tokenVO.setToken(loginToken);
        tokenVO.setLocate(dto.getLocal());
        tokenVO.setLoginDate(LocalDateTime.now());
        tokenVO.setPersonId(person.getId());
        tokenVO.setRoleType(dto.getRoleType());
        //生成用户员工信息
        LoginVO loginVO = LoginUtil.buildLogin(login,dto,person,customer);
        loginVO.setSysRoles(beanMapper.mapAsList(sysRoles, SysRoleVO.class));
        loginVO.setLocate(dto.getLocal());
        loginVO.setRoleType(dto.getRoleType());

        String authPersonKey = LoginConstant.AUTH.concat(login.getPersonId().toString());

        //此处需考虑员工的角色或其他的变化修改信息，所以不直接把员工信息放到token对应的值
        List<String> keys = Lists.newArrayList(LoginConstant.TOKEN.concat(loginToken),authPersonKey);
        //为保证原子性，采用lua脚本实现
        Long luaResult = (Long)stringRedisTemplate.execute(this.loginScript,new StringRedisSerializer(),new StringRedisSerializer(),keys,JSONObject.toJSONString(tokenVO), JSONObject.toJSONString(loginVO),loginHelper.loginExpireTime() + "");
        TsfPreconditions.checkArgument(1 == luaResult,new ServiceException(ResultCodeEnum.BUSINESS_ERROR));
        if(Objects.nonNull(customer)){
            customerService.refreshCacheCompany(customer);
        }
        //记录登录日志
        asyncLogLogin(person,dto.getLoginType(),dto.getLocal());
        return new LoginInfoVO(loginToken,loginVO);
    }
    //同步人员信息
    @Transactional(rollbackFor = Exception.class)
    public void synchroPerson(Person person,ThreeLoginVO threeLoginVO){
        Person hPerson = beanMapper.map(person,Person.class);
        if(StringUtils.isEmpty(person.getHead()) && StringUtils.isNotEmpty(threeLoginVO.getAvatarUrl())){
            person.setHead(threeLoginVO.getAvatarUrl());
        }
        if(person.getName().startsWith("新用户") && StringUtils.isNotEmpty(threeLoginVO.getNickName())){
            person.setName(threeLoginVO.getNickName());
        }
        if(SexTypeEnum.UNKNOW.getCode().equals(person.getGender()) && StringUtils.isNotEmpty(threeLoginVO.getGender())){
            person.setGender(threeLoginVO.getGender());
        }
        if("wxxcx".equals(threeLoginVO.getSource())){
            //微信小程序
            person.setWxxcxOpenid(threeLoginVO.getOpenId());
        }else if("wxgzh".equals(threeLoginVO.getSource())){
            //微信公众号
            person.setWxgzhOpenid(threeLoginVO.getOpenId());
        }
        if(!hPerson.toString().equals(person.toString())){
            //数据有变化需要更新
            personService.updateById(person);
        }
    }

    @Override
    public SGTLoginVO sgtLogin(SGTLoginDTO dto) {
        // 1、获取登录账号信息
        Login login = loginMapper.selectByLoginName(dto.getUserName());
        TsfPreconditions.checkNonNull(login,"当前账号不存在");
        // 2、获取登录人员信息
        Person person = personService.getById(login.getPersonId());
        TsfPreconditions.checkNonNull(person,"登录帐号信息错误");
        if(!Objects.equals(person.getPassWord(),StringUtils.encrySha((dto.getPassWord())))) {
            throw new ServiceException("登录密码输入错误");
        }
        TsfPreconditions.checkArgument(Objects.equals(person.getIncumbency(),Boolean.TRUE),new ServiceException("您已禁止登录该系统"));
        TsfPreconditions.checkArgument(!Objects.equals(person.getIsDelete(),Boolean.TRUE),new ServiceException("您已被该企业移除，禁止登录该系统"));
        // 获取客户信息
        Customer customer = customerService.getById(person.getCustomerId());
        TsfPreconditions.checkNonNull(customer,"当前账号公司信息不存在");
        TsfPreconditions.checkArgument(Objects.equals(customer.getDisabled(),Boolean.FALSE),new ServiceException("您的公司信息已被禁用"));
        // 根据客户ID和海关编码获取备案企业信息
        SGTLoginVO vo = new SGTLoginVO();
        vo.setCustomerId(customer.getId());
        vo.setCustomerName(customer.getName());
        vo.setPersonName(person.getName());
        Result<RecordEnterprisesVO> result = declareServerClient.getEnterprise(vo.getCustomerId(),dto.getCustomsCode());
        TsfPreconditions.checkNonNull(result,"获取备案企业信息失败，请稍后重试");
        if(!result.isSuccess()){
            throw new ServiceException(result.getMessage());
        }
        Boolean enterpriseDisabledFlag = result.getData().getDisabled();
        if(Objects.equals(enterpriseDisabledFlag,Boolean.TRUE)) {
            throw new ServiceException("电商备案企业已被禁用，您暂时无法登陆");
        }
        vo.setEnterpriseName(result.getData().getName());
        return vo;
    }

    @Override
    public LoginInfoVO wxxcxAuthorize(WxxcxLoginDTO dto) {
        Map<String,Object> params = new LinkedHashMap<>();
        params.put("appid",wxccxAppid);
        params.put("secret",wxccxSecret);
        params.put("js_code",dto.getCode());
        params.put("grant_type","authorization_code");
        String response = HttpUtil.get(WeiXinConstant.jscode2session,params);
        Map<String,String> map = JSON.parseObject(response, Map.class);
        JSONObject json = WXUtil.getUserInfo(dto.getEncryptedData(),map.get("session_key"),dto.getIv());
        ThreeLoginVO vo = beanMapper.map(json, ThreeLoginVO.class);
        vo.setSource("wxxcx");//微信小程序
        if("1".equals(vo.getGender())){//性别
            vo.setGender(SexTypeEnum.MAN.getCode());
        }else if("2".equals(vo.getGender())){
            vo.setGender(SexTypeEnum.FEMALE.getCode());
        }else{
            vo.setGender(SexTypeEnum.UNKNOW.getCode());
        }
        //临时存入缓存
        LoginInfoVO loginInfoVO = new LoginInfoVO();
        loginInfoVO.setBindAccessId(StringUtils.UUID());
        redisUtils.set(LoginConstant.TEMP_LOGIN.concat(loginInfoVO.getBindAccessId()),vo,Long.valueOf(1), TimeUnit.HOURS);
        LoginDTO loginDTO = new LoginDTO();
        loginDTO.setLoginType(LoginTypeEnum.WXXCX.getCode());
        loginDTO.setRoleType(dto.getRoleType());
        loginDTO.setUserName(vo.getUnionId());
        loginDTO.setBindAccessId(loginInfoVO.getBindAccessId());
        return login(loginDTO);
    }
    @Override
    public LoginInfoVO wxxcxAuthorizeByPhone(WxxcxLoginDTO dto) {
        Map<String,Object> params = new LinkedHashMap<>();
        params.put("appid",wxccxAppid);
        params.put("secret",wxccxSecret);
        params.put("js_code",dto.getCode());
        params.put("grant_type","authorization_code");
        String response = HttpUtil.get(WeiXinConstant.jscode2session,params);
        Map<String,String> map = JSON.parseObject(response, Map.class);
        JSONObject json = WXUtil.getUserInfo(dto.getEncryptedData(),map.get("session_key"),dto.getIv());
        if(Objects.isNull(json)) {
            throw new ServiceException("微信登录已失效，请重新登录");
        }
        WxPhoneLoginVO vo = beanMapper.map(json, WxPhoneLoginVO.class);
        LoginDTO loginDTO = new LoginDTO();
        loginDTO.setLoginType(LoginTypeEnum.WXXCX_PHONE.getCode());
        loginDTO.setRoleType(dto.getRoleType());
        loginDTO.setUserName(vo.getPurePhoneNumber());
        loginDTO.setBindAccessId(dto.getBindAccessId());
        return login(loginDTO);
    }

    @Override
    public void syncWxHeadAndName(WxxcxLoginDTO dto) {
        Map<String,Object> params = new LinkedHashMap<>();
        params.put("appid",wxccxAppid);
        params.put("secret",wxccxSecret);
        params.put("js_code",dto.getCode());
        params.put("grant_type","authorization_code");
        String response = HttpUtil.get(WeiXinConstant.jscode2session,params);
        Map<String,String> map = JSON.parseObject(response, Map.class);
        JSONObject json = WXUtil.getUserInfo(dto.getEncryptedData(),map.get("session_key"),dto.getIv());
        ThreeLoginVO vo = beanMapper.map(json, ThreeLoginVO.class);
        vo.setSource("wxxcx");//微信小程序
        if("1".equals(vo.getGender())){//性别
            vo.setGender(SexTypeEnum.MAN.getCode());
        }else if("2".equals(vo.getGender())){
            vo.setGender(SexTypeEnum.FEMALE.getCode());
        }else{
            vo.setGender(SexTypeEnum.UNKNOW.getCode());
        }
        personService.updatePersonByWeixin(vo);
    }

    @Override
    public String checkUserBindFocusWxUnion() {
        Long personId = SecurityUtil.getCurrentPersonId();
        Long customerId = SecurityUtil.getCurrentCustomerId();
        String personName = SecurityUtil.getCurrentPersonName();
        Boolean showQrCode = false;
        Boolean bindWxLogin = loginMapper.selectCountByExample(Example.builder(Login.class).where(TsfWeekendSqls.<Login>custom()
                .andEqualTo(false,Login::getPersonId,SecurityUtil.getCurrentPersonId())
                .andEqualTo(false,Login::getType,4)).build()) > 0;
        //此处有可能没有绑定微信平台统一登录；绑定了微信统一平台，但取消了关注，即人员表中无微信公众号
        if(false == bindWxLogin) {
            log.info("当前登录人员【{}】【{}】没有绑定微信平台登录",personId,personName);
            showQrCode = true;
        } else {
            log.info("当前登录人员【{}】【{}】绑定了微信平台登录，检查是否有关注微信公众平台",personId,personName);
            Person person = personService.getPhoneByCustomerId(customerId);
            showQrCode = Objects.isNull(person) || StringUtils.isEmpty(person.getWxgzhOpenid());
        }
        String bindId = showQrCode == false ? "" :  LoginConstant.BIND_WEIXIN_PREFIX.concat(StrUtil.uuid().replace("-",""));
        if(StringUtils.isNotEmpty(bindId)) {
            //便于后续人员关联
            ThreeLoginVO threeLoginVO = new ThreeLoginVO();
            threeLoginVO.setPersonId(personId);
            redisUtils.set(LoginConstant.BIND_WEIXIN.concat(bindId),threeLoginVO,Long.valueOf(1), TimeUnit.HOURS);
        }
        return bindId;
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public WeixinFocusScanVO checkBindFocusWxgzh(String wxCode) {
        Long personId = SecurityUtil.getCurrentPersonId();
        String personName = SecurityUtil.getCurrentPersonName();
        Person person = personService.getById(personId);
        //判断是否绑定登录，如果绑定了登录但是取消了关注也一并处理
        Boolean bindWxLogin = false;
        Login thirdLogin =  loginMapper.selectOneByExample(Example.builder(Login.class).where(TsfWeekendSqls.<Login>custom()
                .andEqualTo(false,Login::getPersonId,personId)
                .andEqualTo(false,Login::getType,4)).build());
        if(Objects.nonNull(thirdLogin)) {
            bindWxLogin = true;
        }
        if(bindWxLogin) {
            if(Objects.nonNull(person) && StringUtils.isNotEmpty(person.getWxgzhOpenid())) {
                throw new ServiceException("您已经绑定关注了xxxx微信公众号，无法绑定关注");
            }
        }
        Object threeLoginVOObj = redisUtils.get(LoginConstant.BIND_WEIXIN.concat(wxCode));
        if(Objects.nonNull(threeLoginVOObj)) {
            ThreeLoginVO threeLoginVO = beanMapper.map(threeLoginVOObj, ThreeLoginVO.class);
            if(Objects.nonNull(threeLoginVO.getPersonId()) && !Objects.equals(personId.toString(),threeLoginVO.getPersonId().toString())) {
                log.info("当前登录人员id【{}】,缓存的人员id【{}】不匹配",personId,threeLoginVO.getPersonId());
                //删除缓存，前端重新获取
                redisUtils.remove(LoginConstant.BIND_WEIXIN.concat(wxCode));
                return new WeixinFocusScanVO(5,"操作非法，请重新扫码操作");
            }
            if(Objects.nonNull(threeLoginVO.getPersonId())
               && StringUtils.isNotEmpty(threeLoginVO.getUnionId())){ //用户扫码操作了
                //有可能是其他用户扫码
                Login checkLogin = loginMapper.selectByLoginName(threeLoginVO.getUnionId());
                Boolean wxNotice = Boolean.FALSE;//是否微信登录通知
                if(Objects.isNull(checkLogin)) { //且当前登录用户没有绑定登录信，扫码的用户也没有绑定微信登录
                    if(bindWxLogin == false) {
                        log.info("扫码的设备用户【{}】没有绑定微信登录，登录用户也没用绑定微信登录",threeLoginVO.getUnionId());
                        Login login = new Login();
                        login.setPersonId(personId);
                        login.setLoginName(threeLoginVO.getUnionId());
                        login.setType(4);
                        try {
                            super.saveNonNull(login);
                            wxNotice = Boolean.TRUE;
                        } catch (DuplicateKeyException e) {
                            throw new ServiceException("您已经绑定了xxxx微信公众号，无法绑定");
                        }
                    } else {
                        log.info("扫码的设备用户【{}】没有绑定微信登录，登录用户【{}】已经绑定微信登录",threeLoginVO.getUnionId(),thirdLogin.getLoginName());
                        //判断是否同一用户操作，通过unionid判断
                        if(!Objects.equals(thirdLogin.getLoginName(),threeLoginVO.getUnionId())) {
                            //删除缓存，前端重新获取
                            redisUtils.remove(LoginConstant.BIND_WEIXIN.concat(wxCode));
                            return new WeixinFocusScanVO(4,"请使用您登录帐号绑定的微信扫码操作");
                        }
                    }
                } else {
                    //防止是其他设备扫码，需判断是否同一个帐号操作
                    log.info("扫码的设备用户【{}】已经有绑定微信登录，直接更新人员表微信公众号信息，检查是否同一用户扫码操作，扫码人员【{}】,待绑人员【{}】",threeLoginVO.getUnionId(),checkLogin.getPersonId(),threeLoginVO.getPersonId());
                    if(!Objects.equals(checkLogin.getPersonId().toString(),threeLoginVO.getPersonId().toString())){
                        //删除缓存，前端重新获取
                        redisUtils.remove(LoginConstant.BIND_WEIXIN.concat(wxCode));
                        return new WeixinFocusScanVO(4,"请使用您登录帐号绑定的微信扫码操作");
                    }
                    //操作用户未绑定微信登录，扫码用户绑定了微信登录，且是同一个用户，则直接更新人员表的微信公众号即可

                }
                //更新人员表的微信公众号字段
                personService.subscribeWxgzh(threeLoginVO.getPersonId(),threeLoginVO.getOpenId());
                redisUtils.remove(LoginConstant.BIND_WEIXIN.concat(wxCode));
                //发送微信公众号消息
                if(Objects.equals(wxNotice,Boolean.TRUE)) {
                    bindWxLoginNotice(person,threeLoginVO);
                }
                return new WeixinFocusScanVO(2,"绑定成功，后续您的订单动态信息将通过微信公众平台推送给您");
            } else {
                log.info("用户【{}】【{}】还未扫码操作",personId,personName);
                return new WeixinFocusScanVO(3,"用户还未扫码操作");
            }
        } else {
            log.info("扫描二维码已失效");
            return new WeixinFocusScanVO(1,"二维码已失效，请重新使用微信扫码操作");
        }
    }

    @Override
    public Boolean checkUserBindWxLogin(Long personId) {
        Login thirdLogin =  loginMapper.selectOneByExample(Example.builder(Login.class).where(TsfWeekendSqls.<Login>custom()
                .andEqualTo(false,Login::getPersonId,personId)
                .andEqualTo(false,Login::getType,4)).build());
        return Objects.nonNull(thirdLogin);
    }

    @Override
    public String generateWxLoginKey(String operateType) {
        Long personId = SecurityUtil.getCurrentPersonId();
        //判断用户是否绑定了微信登录
        Boolean bindWxLogin = checkUserBindWxLogin(personId);
        switch (operateType) {
            case "bind":
                if(bindWxLogin) {
                    throw new ServiceException("您已经绑定了微信，无需再次进行绑定");
                }
                break;
            case "changeBind":
                if(!bindWxLogin){
                    throw new ServiceException("您还未绑定微信，请先绑定微信再尝试解绑");
                }
                break;
            default:
                throw new ServiceException("请求非法");
        }
        String bindId = LoginConstant.BIND_WEIXIN_LOGIN_PREFIX.concat(StrUtil.uuid().replace("-",""));
        if(StringUtils.isNotEmpty(bindId)) {
            //便于后续人员关联判断等
            ThreeLoginVO threeLoginVO = new ThreeLoginVO();
            threeLoginVO.setPersonId(personId);
            String cacheKey = LoginConstant.BIND_WEIXIN_LOGIN.concat(bindId);
            log.info("写入缓存key【{}】",cacheKey);
            redisUtils.set(cacheKey,threeLoginVO,Long.valueOf(1), TimeUnit.HOURS);
        }
        return bindId;
    }

    @Override
    public WeixinFocusScanVO checkBindWxgzh(String operateType,String wxCode) {
        Long personId = SecurityUtil.getCurrentPersonId();
        String personName = SecurityUtil.getCurrentPersonName();
        if(!Objects.equals(operateType,"bind")
           && !Objects.equals(operateType,"changeBind")) {
            throw new ServiceException("请求非法");
        }
        Person person = personService.getById(personId);
        //判断是否绑定登录，仅仅判断登录，不判断关注和取消关注
        Login thirdLogin =  loginMapper.selectOneByExample(Example.builder(Login.class).where(TsfWeekendSqls.<Login>custom()
                .andEqualTo(false,Login::getPersonId,personId)
                .andEqualTo(false,Login::getType,4)).build());
        if(Objects.equals(operateType,"bind")
           && Objects.nonNull(thirdLogin)) {
            throw new ServiceException("您已经绑定xxxx微信公众号登录，无法再次绑定，如需再次绑定，请刷新页面进行换绑");
        }
        if(Objects.equals(operateType,"changeBind")
                && Objects.isNull(thirdLogin)) {
            throw new ServiceException("您还未绑定xxxx微信公众号登录，无法进行换绑，请刷新页面进行绑定");
        }
        Object threeLoginVOObj = redisUtils.get(LoginConstant.BIND_WEIXIN_LOGIN.concat(wxCode));
        if(Objects.nonNull(threeLoginVOObj)) {
            ThreeLoginVO threeLoginVO = beanMapper.map(threeLoginVOObj, ThreeLoginVO.class);
            if(Objects.nonNull(threeLoginVO.getPersonId()) && !Objects.equals(personId.toString(),threeLoginVO.getPersonId().toString())) {
                log.info("当前登录人员id【{}】,缓存的人员id【{}】不匹配",personId,threeLoginVO.getPersonId());
                //删除缓存，前端重新获取
                redisUtils.remove(LoginConstant.BIND_WEIXIN_LOGIN.concat(wxCode));
                return new WeixinFocusScanVO(5,"操作非法，请重新扫码操作");
            }
            if(Objects.nonNull(threeLoginVO.getPersonId())
                    && StringUtils.isNotEmpty(threeLoginVO.getUnionId())){ //用户扫码操作了
                //有可能是其他用户扫码
                Login checkLogin = loginMapper.selectByLoginName(threeLoginVO.getUnionId());
                if(Objects.equals("bind",operateType)) {
                    //绑定微信
                    //扫码的用户也没有绑定微信
                    if(Objects.isNull(checkLogin)) {
                        log.info("扫码的设备用户【{}】没有绑定微信登录，登录用户也没用绑定微信登录",threeLoginVO.getUnionId());
                        Login login = new Login();
                        login.setPersonId(personId);
                        login.setLoginName(threeLoginVO.getUnionId());
                        login.setType(4);
                        try {
                            super.saveNonNull(login);
                            personService.subscribeWxgzh(personId,threeLoginVO.getOpenId());
                            redisUtils.remove(LoginConstant.BIND_WEIXIN_LOGIN.concat(wxCode));
                            bindWxLoginNotice(person,threeLoginVO);
                            return new WeixinFocusScanVO(2,"绑定成功，后续您可以使用微信扫码登录");
                        } catch (DuplicateKeyException e) {
                            redisUtils.remove(LoginConstant.BIND_WEIXIN_LOGIN.concat(wxCode));
                            throw new ServiceException("您已经绑定了xxxx微信公众号，无法绑定");
                        }
                    } else {
                        redisUtils.remove(LoginConstant.BIND_WEIXIN_LOGIN.concat(wxCode));
                        throw new ServiceException("微信扫码的用户已经进行了绑定，无法再次绑定");
                    }
                } else if (Objects.equals("changeBind",operateType)) {
                    //换绑微信
                    //扫码的用户没有绑定微信
                    if(Objects.isNull(checkLogin)) {
                        log.info("扫码的设备用户【{}】没有绑定微信登录，登录用户绑定微信登录",threeLoginVO.getUnionId());
                        Login update = new Login();
                        update.setLoginName(threeLoginVO.getUnionId());
                        int updateCnt = loginMapper.updateByExampleSelective(update, Example.builder(Login.class).where(
                                WeekendSqls.<Login>custom()
                                        .andEqualTo(Login::getPersonId,personId)
                                        .andEqualTo(Login::getId, thirdLogin.getId())).build());
                        if(updateCnt == 1) {
                            //更新人员表中的公众号
                            personService.subscribeWxgzh(personId,threeLoginVO.getOpenId());
                            redisUtils.remove(LoginConstant.BIND_WEIXIN_LOGIN.concat(wxCode));
                            bindWxLoginNotice(person,threeLoginVO);
                            return new WeixinFocusScanVO(2,"换绑成功，后续您可以使用换绑的微信扫码登录");
                        } else {
                            redisUtils.remove(LoginConstant.BIND_WEIXIN_LOGIN.concat(wxCode));
                            return new WeixinFocusScanVO(4,"换绑失败，请稍后再试");
                        }
                    } else {
                        log.info("扫码的设备用户【{}】绑定微信登录，登录用户也绑定微信登录，检查是否同一人操作",threeLoginVO.getUnionId());
                        if(Objects.equals(thirdLogin.getLoginName(),threeLoginVO.getUnionId())) {
                            log.info("您已经绑定了xxxx微信公众号，如需换绑，请使用其他微信帐号换绑");
                            redisUtils.remove(LoginConstant.BIND_WEIXIN_LOGIN.concat(wxCode));
                            return new WeixinFocusScanVO(6,"请使用其他微信帐号扫码操作");
                        } else {
                            redisUtils.remove(LoginConstant.BIND_WEIXIN_LOGIN.concat(wxCode));
                            throw new ServiceException("微信扫码的用户已经绑定了其他帐号，无法再次绑定");
                        }
                    }
                } else {
                    throw new ServiceException("请求非法");
                }
            } else {
                log.info("用户【{}】【{}】还未扫码操作",personId,personName);
                return new WeixinFocusScanVO(3,"用户还未扫码操作");
            }
        } else {
            log.info("扫描二维码已失效");
            return new WeixinFocusScanVO(1,"二维码已失效，请重新使用微信扫码操作");
        }
    }

    public void bindWxLoginNotice(Person person,ThreeLoginVO threeLoginVO) {
        //发送微信公众号消息
        WxNoticeMessageDTO wxNoticeMessageDTO = new WxNoticeMessageDTO();
        wxNoticeMessageDTO.setWxgzhOpenid(threeLoginVO.getOpenId());
        wxNoticeMessageDTO.setWxMessageDefineTemplate(WxMessageDefineTemplate.BIND_WX);
        wxNoticeMessageDTO.setIsNavToMiniprogram(Boolean.FALSE);
        LinkedHashMap<String, WxNoticeAttr> params = new LinkedHashMap<>();
        params.put("first",new WxNoticeAttr("尊敬的用户，您已成功绑定微信登录和通知","#04BE02"));
        params.put("keyword1",new WxNoticeAttr(person.getPhone(),"#9C9C9C"));
        params.put("keyword2",new WxNoticeAttr(LocalDateTimeUtils.formatTime(LocalDateTime.now(),"yyyy-MM-dd HH:mm:ss"),"#9C9C9C"));
        params.put("keyword3",new WxNoticeAttr(StrUtil.format("您绑定的微信昵称为{}，后续您可以使用微信扫码登录，并随时接收相关微信通知",threeLoginVO.getNickName()),"#9C9C9C"));
        params.put("remark",new WxNoticeAttr("如您需要换绑，请至电脑端操作"));
        wxNoticeMessageDTO.setParams(params);
        wxMessageService.sendWxNotice(wxNoticeMessageDTO);
    }

    @Override
    public LoginInfoVO wxGzhCheckLogin(WxgzhLoginDTO dto) {
        Object threeLoginVOObj = redisUtils.get(LoginConstant.TEMP_LOGIN.concat(dto.getBindAccessId()));
        if(Objects.nonNull(threeLoginVOObj)) {
            ThreeLoginVO threeLoginVO = beanMapper.map(threeLoginVOObj, ThreeLoginVO.class);
            LoginDTO loginDTO = new LoginDTO();
            loginDTO.setLoginType(LoginTypeEnum.WXGZH.getCode());
            loginDTO.setRoleType(dto.getRoleType());
            loginDTO.setUserName(threeLoginVO.getUnionId());
            loginDTO.setBindAccessId(dto.getBindAccessId());
            return login(loginDTO);
        }
        return null;
    }

    /**
     * 异步记录登录日志
     * @param finalPerson
     * @param loginType
     */
    public void asyncLogLogin(final Person finalPerson,Integer loginType,final String local) {
        final String ip = RequestUtils.getIp();
        final String device = RequestUtils.obtainDevice();
        final String userAgent = RequestUtils.obtainUserAgent();
        threadPoolTaskExecutor.execute(()->{
            LogLoginDTO logLoginDTO = new LogLoginDTO();
            logLoginDTO.setPersonId(finalPerson.getId());
            logLoginDTO.setPersonName(finalPerson.getName());
            logLoginDTO.setIp(ip);
            logLoginDTO.setAddress(local);
            logLoginDTO.setDevice(device);
            logLoginDTO.setLoginType(loginType);
            logLoginDTO.setUserAgent(userAgent);
            logLoginService.save(logLoginDTO);
        });
    }

    @Override
    public void logout() {
        String token = request.getHeader("token");
        if(StringUtils.isNotEmpty(token)) {
            stringRedisUtils.remove(LoginConstant.TOKEN.concat(token));
        }
    }

    @Override
    public void updateLogin(Login login) {
        //不修改密码
        Example example = new Example(Login.class);
        example.createCriteria().andEqualTo("type",login.getType())
                                .andEqualTo("personId",login.getPersonId());
        Login updateLogin = new Login();
        updateLogin.setLoginName(login.getLoginName());
        try {
            loginMapper.updateByExampleSelective(updateLogin,example);
        } catch (DuplicateKeyException e) {
            if(e.getMessage().contains("uni_type_login_name")) {
                throw new ServiceException("员工编码已经存在");
            }
        }
    }

    @Override
    public void deleteByPersonId(Long personId) {
        Login condition = new Login();
        condition.setPersonId(personId);
        loginMapper.delete(condition);
    }

    @Override
    public void forceLogout(String personId,String personName) {
        if(StringUtils.isNotEmpty(personId)) {
            stringRedisUtils.remove(LoginConstant.AUTH + personId);
            log.debug("用户id:{}，名称:{}强制退出成功",personId,personName);
        }
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public void updatePassWordForManager(UpdatePassWordDTO dto) {
        TsfPreconditions.checkArgument(Objects.nonNull(dto.getPersonId()),new ServiceException("请选择员工"));
        //如果是客户端客户则不允许修改
        Person person = personService.getById(dto.getPersonId());
        Optional.ofNullable(person).orElseThrow(()->new ServiceException("员工信息不存在"));
        if(Objects.equals(person.getIsClient(),Boolean.TRUE)
           || Objects.equals(person.getIsSale(),Boolean.TRUE) ){
            throw new ServiceException("不允许在管理端修改非内部员工密码");
        }
        dto.setNewPassWord(StringUtils.encrySha(dto.getNewPassWord()));
        //修改员工需要重置密码标识
        personService.updatePersonPwdChangeFlag(dto.getNewPassWord(),dto.getPersonId());
        //修改密码需要退出登录
        stringRedisUtils.remove(LoginConstant.AUTH + dto.getPersonId().toString());
    }

    @Override
    public void saveLogin(Long personId, Integer type, String loginName) {
        Login login = new Login();
        login.setPersonId(personId);
        login.setType(type);
        login.setLoginName(loginName);
        loginMapper.insertSelective(login);
    }

    @Override
    public ClientInfoVO clientInfo() {
        LoginVO loginVO =  SecurityUtil.getCurrent();
        if(Objects.nonNull(loginVO)){
            ClientInfoVO info = beanMapper.map(loginVO,ClientInfoVO.class);
            CompanyVO company = customerService.getCacheCompany(info.getCustomerId());
            info.setCustomerName(company.getName());
            info.setStatus(company.getStatusId());
            info.setStatusName(company.getStatusName());
            return info;
        }
        return null;
    }

    @Override
    public Login findByLoginName(String phoneNo) {
        Login query = new Login();
        query.setLoginName(phoneNo);
        return super.getOne(query);
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public void retrievePassword(ClientUpdatePassWordDTO dto) {
        Login login = findByLoginName(dto.getPhoneNo());
        TsfPreconditions.checkNonNull(login,"当前手机号未注册，您可以直接注册登录");
        //获取人员信息
        Person person = personService.getById(login.getPersonId());
        TsfPreconditions.checkNonNull(person,"登录帐号信息错误");
        //验证短信验证码
        shortMessageService.checkVerificationCode(dto.getPhoneNo(),MessageNodeEnum.COMMON_PROVE,dto.getValidateCode());
        person.setPassWord(StringUtils.encrySha(dto.getPassword()));
        if(!Objects.equals(person.getIsChangedPwd(),Boolean.TRUE)) {
            person.setIsChangedPwd(Boolean.TRUE);
        }
        personService.updateById(person);
        //退出该人员所有的登录信息，直接清除人员信息，重新登录
        stringRedisUtils.remove(LoginConstant.AUTH + person.getId());
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public void updateLoginPhone(Long personId, String originPhoneNo, String newPhoneNo) {
        Login updateLogin = new Login();
        updateLogin.setLoginName(newPhoneNo);
        loginMapper.updateByExampleSelective(updateLogin, Example.builder(Login.class).where(
                TsfWeekendSqls.<Login>custom().andEqualTo(false,Login::getPersonId, personId)).build());
        //退出该人员所有的登录信息，直接清除人员信息，重新登录
        stringRedisUtils.remove(LoginConstant.AUTH + personId);
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public void clientModifyPassWord(ClientChangePassWordDTO dto) {
        Long personId = SecurityUtil.getCurrentPersonId();
        TsfPreconditions.checkArgument(Objects.equals(dto.getNewPassword(),dto.getConfirmPassword()),new ServiceException("两次密码输入不一致"));
        //获取人员信息
        Person person = personService.getById(personId);
        TsfPreconditions.checkNonNull(person,"登录帐号信息错误");
        //校验旧密码是否正确
        TsfPreconditions.checkArgument(Objects.equals(person.getPassWord(),StringUtils.encrySha(dto.getPassword())),new ServiceException("旧密码输入错误"));
        //新旧密码不能一致
        TsfPreconditions.checkArgument(!Objects.equals(dto.getNewPassword(),dto.getPassword()),new ServiceException("新旧密码不能一样"));
        person.setPassWord(StringUtils.encrySha(dto.getNewPassword()));
        person.setIsChangedPwd(Boolean.FALSE);
        personService.updateById(person);
        //退出该人员所有的登录信息，直接清除人员信息，重新登录
        stringRedisUtils.remove(LoginConstant.AUTH + person.getId());
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public void clientSetPassWord(ClientSetPassWordDTO dto) {
        Long personId = SecurityUtil.getCurrentPersonId();
        TsfPreconditions.checkArgument(Objects.equals(dto.getNewPassword(),dto.getConfirmPassword()),new ServiceException("两次密码输入不一致"));
        //获取人员信息
        Person person = personService.getById(personId);
        TsfPreconditions.checkNonNull(person,"登录帐号信息错误");
        TsfPreconditions.checkArgument(StringUtils.isEmpty(person.getPassWord()),new ServiceException("您已经设置过登录密码"));
        person.setPassWord(StringUtils.encrySha(dto.getNewPassword()));
        person.setIsChangedPwd(Boolean.FALSE);
        personService.updateById(person);
        //退出该人员所有的登录信息，直接清除人员信息，重新登录
        stringRedisUtils.remove(LoginConstant.AUTH + person.getId());
    }

    @Override
    public Map<Long, CustomerLoginInfoVO> allCustomerLoginInfo() {
        List<CustomerLoginInfoVO> dataList = loginMapper.allCustomerLoginInfo();
        if(CollUtil.isNotEmpty(dataList)){
            return dataList.stream().collect(Collectors.toMap(CustomerLoginInfoVO::getCustomerId, Function.identity()));
        }
        return new LinkedMap<>();
    }
}
